package com.wxt.asmplugins

import org.gradle.api.Project
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes

/**
 * Created by wanglei on 2017/3/15.
 */
class FragmentInjector extends ClassVisitor implements Opcodes {
    private final Project project
    private HashMap<String, Integer> methodMap;

    FragmentInjector(ClassVisitor classVisitor, Project project) {
        super(Opcodes.ASM5,classVisitor)
        this.project = project
        methodMap = new HashMap<>()
        methodMap.put("onResume", 0)
        methodMap.put("onPause", 1)
        methodMap.put("onHiddenChanged", 2)
        methodMap.put("setUserVisibleHint", 3)
    }

    @Override
    MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        MethodVisitor wrappedMv = mv;
        if (mv != null) {
            if (name.equals("onResume") && desc.equals("()V")) {
                wrappedMv.visitCode()
                wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)
                wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wxt/asmtest/Test",
                        "onFragmentResumed", "(Landroid/app/Fragment;)V")
                methodMap.remove("onResume")
            } else if (name.equals("onPause") && desc.equals("()V")) {
                wrappedMv.visitCode()
                wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)
                wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wxt/asmtest/Test",
                        "onFragmentPaused", "(Landroid/app/Fragment;)V")
                methodMap.remove("onPause")
            } else if (name.equals("onHiddenChanged") && desc.equals("(Z)V")) {
                wrappedMv.visitCode()
                wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)
                wrappedMv.visitVarInsn(ILOAD, 1)
                wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wxt/asmtest/Test",
                        "onFragmentHiddenChanged", "(Landroid/app/Fragment;Z)V")
                methodMap.remove("onHiddenChanged")
            } else if (name.equals("setUserVisibleHint") && desc.equals("(Z)V")) {
                wrappedMv.visitCode()
                wrappedMv.visitVarInsn(Opcodes.ALOAD, 0)
                wrappedMv.visitVarInsn(ILOAD, 1)
                wrappedMv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/wxt/asmtest/Test",
                        "setFragmentUserVisibleHint", "(Landroid/app/Fragment;Z)V")
                methodMap.remove("setUserVisibleHint")
            }
        }
        return wrappedMv;
    }

    @Override
    void visitEnd() {
        ArrayList<Integer> unInjectMethods = new ArrayList<>();
        unInjectMethods.addAll(methodMap.values())
        for (Integer index : unInjectMethods) {
            generateMethod(index)
        }
        super.visitEnd()
    }

    private void generateMethod(int methodIndex) {
        MethodVisitor mv;
        switch (methodIndex) {
            case 0:
                mv = cv.visitMethod(ACC_PUBLIC, "onResume", "()V", null, null);
                mv.visitCode();
                mv.visitVarInsn(ALOAD, 0);
                mv.visitMethodInsn(INVOKESTATIC, "com/wxt/asmtest/Test", "onFragmentResumed", "(Landroid/app/Fragment;)V");
                mv.visitVarInsn(ALOAD, 0);
                mv.visitMethodInsn(INVOKESPECIAL, "android/app/Fragment", "onResume", "()V");
                mv.visitInsn(RETURN);
                mv.visitMaxs(1, 1);
                mv.visitEnd();
                break;
            case 1:
                mv = cv.visitMethod(ACC_PUBLIC, "onPause", "()V", null, null);
                mv.visitCode();
                mv.visitVarInsn(ALOAD, 0);
                mv.visitMethodInsn(INVOKESTATIC, "com/wxt/asmtest/Test", "onFragmentPaused", "(Landroid/app/Fragment;)V");
                mv.visitVarInsn(ALOAD, 0);
                mv.visitMethodInsn(INVOKESPECIAL, "android/app/Fragment", "onPause", "()V");
                mv.visitInsn(RETURN);
                mv.visitMaxs(1, 1);
                mv.visitEnd();
                break;
            case 2:
                mv = cv.visitMethod(ACC_PUBLIC, "onHiddenChanged", "(Z)V", null, null);
                mv.visitCode();
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ILOAD, 1);
                mv.visitMethodInsn(INVOKESTATIC, "com/wxt/asmtest/Test", "onFragmentHiddenChanged", "(Landroid/app/Fragment;Z)V");
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ILOAD, 1);
                mv.visitMethodInsn(INVOKESPECIAL, "android/app/Fragment", "onHiddenChanged", "(Z)V");
                mv.visitInsn(RETURN);
                mv.visitMaxs(2, 2);
                mv.visitEnd();
                break;
            case 3:
                mv = cv.visitMethod(ACC_PUBLIC, "setUserVisibleHint", "(Z)V", null, null);
                mv.visitCode();
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ILOAD, 1);
                mv.visitMethodInsn(INVOKESTATIC, "com/wxt/asmtest/Test", "setFragmentUserVisibleHint", "(Landroid/app/Fragment;Z)V");
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ILOAD, 1);
                mv.visitMethodInsn(INVOKESPECIAL, "android/app/Fragment", "setUserVisibleHint", "(Z)V");
                mv.visitInsn(RETURN);
                mv.visitMaxs(2, 2);
                mv.visitEnd();
                break;

        }
    }
}
